Task 3: Outlier Detection
As for clustering, many outlier detection algorithms also use distances. Thus, we should normalize the data:
dataset <- data.frame(scale(faithful))
As a side note: Though the faithful dataset is rather simple, it even was used in the summary chapter of a dissertation on outlier detection at our chair.
a) Visualization
You might remember the scatter plot from the clustering task:
library(ggplot2)
Need help? Try Stackoverflow: https://stackoverflow.com/tags/ggplot2
ggplot(data = dataset) +
geom_point(mapping = aes(x = eruptions, y = waiting)) +
coord_fixed() # uniform axis scaling

We can plot univariate densities:
ggplot(data = dataset) +
geom_histogram(mapping = aes(x = eruptions))

ggplot(data = dataset) +
geom_histogram(mapping = aes(x = waiting))

… but also bivariate densities:
ggplot(data = dataset) +
geom_bin2d(mapping = aes(x = eruptions, y = waiting), bins = 10) +
scale_fill_distiller(palette = "Spectral")

With plotly, we can even make the plot interactive:
library(plotly)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 methods overwritten by 'htmltools':
method from
print.html tools:rstudio
print.shiny.tag tools:rstudio
print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
subplot(
plot_ly(dataset, x = ~eruptions, color = I("black"), type = "histogram",
name = "eruptions"),
plotly_empty(type = "scatter", mode = "markers"), # arguments here serve no purpose other than suppressing warnings
plot_ly(dataset, x = ~eruptions, y = ~waiting, type = "histogram2dcontour",
showscale = FALSE, name = "faithful"),
plot_ly(dataset, y = ~waiting, color = I("black"), type = "histogram",
name = "waiting"),
nrows = 2, heights = c(0.2, 0.8), widths = c(0.8, 0.2),
shareX = TRUE, shareY = TRUE
)
`arrange_()` is deprecated as of dplyr 0.7.0.
Please use `arrange()` instead.
See vignette('programming') for more help
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
Okay, enough plotting for now. In our case, the density plots are not really helpful in finding prominent outliers. We can clearly see the two clusters, even in univariate plots, and decreasing density far from the centers. However, this is what we expect from roughly normally distributed data. There are no points which are really far away from the rest. In the scatter plots, we see that there are some points with a rather high distance to the two cluster centers and a rather high distance to other points. “Rather” is a rather fuzzy term, though. Different outlier detection methods try to capture notions of outlierness formally in different ways. However, there often remains the challenge of finding a suitable threshold: When is a point an inlier and when is it an outlier? The definition might depend on the use case rather than some fixed statistical boundaries. As a remedy, many outlier detection algorithms do not return a binary decision, but a continuous outlier score. This allows moving the threshold flexibly.
b) Outlier Detection
Both outlier detection function proposed in the task return a vector of outlier scores. We define a function for creating a scatter plot with outliers highlighted:
plotOutliers <- function(outlierScores) {
plotData <- cbind(dataset, OutlierScore = outlierScores)
ggplot(data = plotData) +
geom_point(mapping = aes(x = eruptions, y = waiting, color = OutlierScore,
size = OutlierScore)) +
coord_fixed() + # uniform axis scaling
scale_color_distiller(palette = "Spectral") +
guides(color = guide_legend(), size = guide_legend()) # joint legend
}
From the implementation perspective, we basically add the outlier scores as another column to the dataset. Next, we use the aesthetics color and size to style points based on their outlier score. Let’s see the function in action:
gridExtra::grid.arrange(
plotOutliers(DDoutlier::KNN_SUM(dataset, k = 5)) + ggtitle("kNN"),
plotOutliers(dbscan::lof(dataset, k = 5)) + ggtitle("LOF"),
nrow = 1, respect = TRUE
)

Another implementation detail: Our plot function returns the plots as objects, which we arrange side-by-side.
We can see some commonalities and some differences between the results of the two methods. For example, points in the centers of the clusters generally have low outlier scores. The top-right-most point has a high outlier score for both methods.
In contrast, several points in the region between the clusters have a high score for kNN, but a low score for LOF. These points are in a region of low density, with comparatively high distances to nearest neighbors. For kNN, only the distance matters, and the same distance scale is considered globally. For LOF, points which have a high kNN distance, but also neighbors with a high kNN distance, get a comparatively low score. This means the local situation plays a bigger role.
In the bottom-left corner, you can see two points with high LOF score, but medium kNN score. For kNN, these points clearly have a higher kNN distance than points in the cluster center. However, they are not far off, resulting in a comparatively low outlier score. For LOF, the neighbors of these points have a clearly lower distance to their neighbors, because they are closer to the center. Thus, the LOF score of the two points under discussion is rather high.
c) Subspace Outliers
To ease comparison, we create a grid with univariate outlier scores, bivariate outlier scores and bivariate density.
gridExtra::grid.arrange(
plotOutliers(DDoutlier::KNN_SUM(dataset$eruptions, k = 5)) +
ggtitle("Outliers for \"eruptions\"") + theme(legend.position = "none"),
plotOutliers(DDoutlier::KNN_SUM(dataset$waiting, k = 5)) +
ggtitle("Outliers for \"waiting\"") + theme(legend.position = "none"),
plotOutliers(DDoutlier::KNN_SUM(dataset, k = 5)) +
ggtitle("Outliers for full space") + theme(legend.position = "none"),
ggplot(data = dataset) +
geom_bin2d(mapping = aes(x = eruptions, y = waiting), bins = 10) +
scale_fill_distiller(palette = "Spectral") +
ggtitle("Density") + theme(legend.position = "none"),
nrow = 2, ncol = 2, respect = TRUE
)

There definitely are points whose outlier score differs between subspaces. For example, the points with the lowest and highest waiting times have a high outlier score regarding waiting, but a lower score regarding eruptions. Also, there is a point in the middle at roughly (-0.4, -0.1), which is quite outlying regarding eruptions (it is the only point in some “vertical corridor” around it), but rather normal regarding waiting (some other points are in same “horizontal corridor” around it). There are also non-trivial outliers. For example, the point at roughly (-1, 0) is perfectly normal in either one-dimensional space, but has a high outlier score in the two-dimensional space.
LS0tCnRpdGxlOiAiRXhlcmNpc2UgNSBvZiBcIkJpZyBEYXRhIEFuYWx5dGljc1wiIChXaW50ZXIgU2VtZXN0ZXIgMjAyMC8yMSkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgVGFzayAzOiBPdXRsaWVyIERldGVjdGlvbgoKQXMgZm9yIGNsdXN0ZXJpbmcsIG1hbnkgb3V0bGllciBkZXRlY3Rpb24gYWxnb3JpdGhtcyBhbHNvIHVzZSBkaXN0YW5jZXMuClRodXMsIHdlIHNob3VsZCBub3JtYWxpemUgdGhlIGRhdGE6CgpgYGB7cn0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKHNjYWxlKGZhaXRoZnVsKSkKYGBgCgpBcyBhIHNpZGUgbm90ZTogVGhvdWdoIHRoZSBgZmFpdGhmdWxgIGRhdGFzZXQgaXMgcmF0aGVyIHNpbXBsZSwgaXQgZXZlbiB3YXMgdXNlZCBpbiB0aGUgc3VtbWFyeSBjaGFwdGVyIG9mIFthIGRpc3NlcnRhdGlvbiBvbiBvdXRsaWVyIGRldGVjdGlvbiBhdCBvdXIgY2hhaXJdKGh0dHA6Ly9keC5kb2kub3JnLzEwLjU0NDUvSVIvMTAwMDEyMDUzNCkuCgojIyBhKSBWaXN1YWxpemF0aW9uCgpZb3UgbWlnaHQgcmVtZW1iZXIgdGhlIHNjYXR0ZXIgcGxvdCBmcm9tIHRoZSBjbHVzdGVyaW5nIHRhc2s6CgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YSA9IGRhdGFzZXQpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGVydXB0aW9ucywgeSA9IHdhaXRpbmcpKSArCiAgY29vcmRfZml4ZWQoKSAjIHVuaWZvcm0gYXhpcyBzY2FsaW5nCmBgYAoKV2UgY2FuIHBsb3QgdW5pdmFyaWF0ZSBkZW5zaXRpZXM6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhc2V0KSArCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0gZXJ1cHRpb25zKSkKZ2dwbG90KGRhdGEgPSBkYXRhc2V0KSArCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0gd2FpdGluZykpCmBgYAoKLi4uIGJ1dCBhbHNvIGJpdmFyaWF0ZSBkZW5zaXRpZXM6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhc2V0KSArCiAgZ2VvbV9iaW4yZChtYXBwaW5nID0gYWVzKHggPSBlcnVwdGlvbnMsIHkgPSB3YWl0aW5nKSwgYmlucyA9IDEwKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpCmBgYAoKV2l0aCBgcGxvdGx5YCwgd2UgY2FuIGV2ZW4gbWFrZSB0aGUgcGxvdCBpbnRlcmFjdGl2ZToKCmBgYHtyfQpsaWJyYXJ5KHBsb3RseSkKc3VicGxvdCgKICBwbG90X2x5KGRhdGFzZXQsIHggPSB+ZXJ1cHRpb25zLCBjb2xvciA9IEkoImJsYWNrIiksIHR5cGUgPSAiaGlzdG9ncmFtIiwKICAgICAgICAgIG5hbWUgPSAiZXJ1cHRpb25zIiksCiAgcGxvdGx5X2VtcHR5KHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpLCAjIGFyZ3VtZW50cyBoZXJlIHNlcnZlIG5vIHB1cnBvc2Ugb3RoZXIgdGhhbiBzdXBwcmVzc2luZyB3YXJuaW5ncwogIHBsb3RfbHkoZGF0YXNldCwgeCA9IH5lcnVwdGlvbnMsIHkgPSB+d2FpdGluZywgdHlwZSA9ICJoaXN0b2dyYW0yZGNvbnRvdXIiLAogICAgICAgICAgc2hvd3NjYWxlID0gRkFMU0UsIG5hbWUgPSAiZmFpdGhmdWwiKSwKICBwbG90X2x5KGRhdGFzZXQsIHkgPSB+d2FpdGluZywgY29sb3IgPSBJKCJibGFjayIpLCB0eXBlID0gImhpc3RvZ3JhbSIsCiAgICAgICAgICBuYW1lID0gIndhaXRpbmciKSwKICBucm93cyA9IDIsIGhlaWdodHMgPSBjKDAuMiwgMC44KSwgd2lkdGhzID0gYygwLjgsIDAuMiksCiAgc2hhcmVYID0gVFJVRSwgc2hhcmVZID0gVFJVRQopCmBgYAoKT2theSwgZW5vdWdoIHBsb3R0aW5nIGZvciBub3cuCkluIG91ciBjYXNlLCB0aGUgZGVuc2l0eSBwbG90cyBhcmUgbm90IHJlYWxseSBoZWxwZnVsIGluIGZpbmRpbmcgcHJvbWluZW50IG91dGxpZXJzLgpXZSBjYW4gY2xlYXJseSBzZWUgdGhlIHR3byBjbHVzdGVycywgZXZlbiBpbiB1bml2YXJpYXRlIHBsb3RzLCBhbmQgZGVjcmVhc2luZyBkZW5zaXR5IGZhciBmcm9tIHRoZSBjZW50ZXJzLgpIb3dldmVyLCB0aGlzIGlzIHdoYXQgd2UgZXhwZWN0IGZyb20gcm91Z2hseSBub3JtYWxseSBkaXN0cmlidXRlZCBkYXRhLgpUaGVyZSBhcmUgbm8gcG9pbnRzIHdoaWNoIGFyZSByZWFsbHkgZmFyIGF3YXkgZnJvbSB0aGUgcmVzdC4KSW4gdGhlIHNjYXR0ZXIgcGxvdHMsIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSBzb21lIHBvaW50cyB3aXRoIGEgcmF0aGVyIGhpZ2ggZGlzdGFuY2UgdG8gdGhlIHR3byBjbHVzdGVyIGNlbnRlcnMgYW5kIGEgcmF0aGVyIGhpZ2ggZGlzdGFuY2UgdG8gb3RoZXIgcG9pbnRzLgoiUmF0aGVyIiBpcyBhIHJhdGhlciBmdXp6eSB0ZXJtLCB0aG91Z2guCkRpZmZlcmVudCBvdXRsaWVyIGRldGVjdGlvbiBtZXRob2RzIHRyeSB0byBjYXB0dXJlIG5vdGlvbnMgb2Ygb3V0bGllcm5lc3MgZm9ybWFsbHkgaW4gZGlmZmVyZW50IHdheXMuCkhvd2V2ZXIsIHRoZXJlIG9mdGVuIHJlbWFpbnMgdGhlIGNoYWxsZW5nZSBvZiBmaW5kaW5nIGEgc3VpdGFibGUgdGhyZXNob2xkOgpXaGVuIGlzIGEgcG9pbnQgYW4gaW5saWVyIGFuZCB3aGVuIGlzIGl0IGFuIG91dGxpZXI/ClRoZSBkZWZpbml0aW9uIG1pZ2h0IGRlcGVuZCBvbiB0aGUgdXNlIGNhc2UgcmF0aGVyIHRoYW4gc29tZSBmaXhlZCBzdGF0aXN0aWNhbCBib3VuZGFyaWVzLgpBcyBhIHJlbWVkeSwgbWFueSBvdXRsaWVyIGRldGVjdGlvbiBhbGdvcml0aG1zIGRvIG5vdCByZXR1cm4gYSBiaW5hcnkgZGVjaXNpb24sIGJ1dCBhIGNvbnRpbnVvdXMgb3V0bGllciBzY29yZS4KVGhpcyBhbGxvd3MgbW92aW5nIHRoZSB0aHJlc2hvbGQgZmxleGlibHkuCgojIyBiKSBPdXRsaWVyIERldGVjdGlvbgoKQm90aCBvdXRsaWVyIGRldGVjdGlvbiBmdW5jdGlvbiBwcm9wb3NlZCBpbiB0aGUgdGFzayByZXR1cm4gYSB2ZWN0b3Igb2Ygb3V0bGllciBzY29yZXMuCldlIGRlZmluZSBhIGZ1bmN0aW9uIGZvciBjcmVhdGluZyBhIHNjYXR0ZXIgcGxvdCB3aXRoIG91dGxpZXJzIGhpZ2hsaWdodGVkOgoKYGBge3J9CnBsb3RPdXRsaWVycyA8LSBmdW5jdGlvbihvdXRsaWVyU2NvcmVzKSB7CiAgcGxvdERhdGEgPC0gY2JpbmQoZGF0YXNldCwgT3V0bGllclNjb3JlID0gb3V0bGllclNjb3JlcykKICBnZ3Bsb3QoZGF0YSA9IHBsb3REYXRhKSArCiAgICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGVydXB0aW9ucywgeSA9IHdhaXRpbmcsIGNvbG9yID0gT3V0bGllclNjb3JlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBPdXRsaWVyU2NvcmUpKSArCiAgICBjb29yZF9maXhlZCgpICsgIyB1bmlmb3JtIGF4aXMgc2NhbGluZwogICAgc2NhbGVfY29sb3JfZGlzdGlsbGVyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKSArCiAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQoKSwgc2l6ZSA9IGd1aWRlX2xlZ2VuZCgpKSAjIGpvaW50IGxlZ2VuZAp9CmBgYAoKRnJvbSB0aGUgaW1wbGVtZW50YXRpb24gcGVyc3BlY3RpdmUsIHdlIGJhc2ljYWxseSBhZGQgdGhlIG91dGxpZXIgc2NvcmVzIGFzIGFub3RoZXIgY29sdW1uIHRvIHRoZSBkYXRhc2V0LgpOZXh0LCB3ZSB1c2UgdGhlIGFlc3RoZXRpY3MgYGNvbG9yYCBhbmQgYHNpemVgIHRvIHN0eWxlIHBvaW50cyBiYXNlZCBvbiB0aGVpciBvdXRsaWVyIHNjb3JlLgpMZXQncyBzZWUgdGhlIGZ1bmN0aW9uIGluIGFjdGlvbjoKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gOH0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoCiAgcGxvdE91dGxpZXJzKEREb3V0bGllcjo6S05OX1NVTShkYXRhc2V0LCBrID0gNSkpICsgZ2d0aXRsZSgia05OIiksCiAgcGxvdE91dGxpZXJzKGRic2Nhbjo6bG9mKGRhdGFzZXQsIGsgPSA1KSkgKyBnZ3RpdGxlKCJMT0YiKSwKICBucm93ID0gMSwgcmVzcGVjdCA9IFRSVUUKKQpgYGAKCkFub3RoZXIgaW1wbGVtZW50YXRpb24gZGV0YWlsOgpPdXIgcGxvdCBmdW5jdGlvbiByZXR1cm5zIHRoZSBwbG90cyBhcyBvYmplY3RzLCB3aGljaCB3ZSBhcnJhbmdlIHNpZGUtYnktc2lkZS4KCldlIGNhbiBzZWUgc29tZSBjb21tb25hbGl0aWVzIGFuZCBzb21lIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHJlc3VsdHMgb2YgdGhlIHR3byBtZXRob2RzLgpGb3IgZXhhbXBsZSwgcG9pbnRzIGluIHRoZSBjZW50ZXJzIG9mIHRoZSBjbHVzdGVycyBnZW5lcmFsbHkgaGF2ZSBsb3cgb3V0bGllciBzY29yZXMuClRoZSB0b3AtcmlnaHQtbW9zdCBwb2ludCBoYXMgYSBoaWdoIG91dGxpZXIgc2NvcmUgZm9yIGJvdGggbWV0aG9kcy4KCkluIGNvbnRyYXN0LCBzZXZlcmFsIHBvaW50cyBpbiB0aGUgcmVnaW9uIGJldHdlZW4gdGhlIGNsdXN0ZXJzIGhhdmUgYSBoaWdoIHNjb3JlIGZvciBrTk4sIGJ1dCBhIGxvdyBzY29yZSBmb3IgTE9GLgpUaGVzZSBwb2ludHMgYXJlIGluIGEgcmVnaW9uIG9mIGxvdyBkZW5zaXR5LCB3aXRoIGNvbXBhcmF0aXZlbHkgaGlnaCBkaXN0YW5jZXMgdG8gbmVhcmVzdCBuZWlnaGJvcnMuCkZvciBrTk4sIG9ubHkgdGhlIGRpc3RhbmNlIG1hdHRlcnMsIGFuZCB0aGUgc2FtZSBkaXN0YW5jZSBzY2FsZSBpcyBjb25zaWRlcmVkIGdsb2JhbGx5LgpGb3IgTE9GLCBwb2ludHMgd2hpY2ggaGF2ZSBhIGhpZ2gga05OIGRpc3RhbmNlLCBidXQgYWxzbyBuZWlnaGJvcnMgd2l0aCBhIGhpZ2gga05OIGRpc3RhbmNlLCBnZXQgYSBjb21wYXJhdGl2ZWx5IGxvdyBzY29yZS4KVGhpcyBtZWFucyB0aGUgbG9jYWwgc2l0dWF0aW9uIHBsYXlzIGEgYmlnZ2VyIHJvbGUuCgpJbiB0aGUgYm90dG9tLWxlZnQgY29ybmVyLCB5b3UgY2FuIHNlZSB0d28gcG9pbnRzIHdpdGggaGlnaCBMT0Ygc2NvcmUsIGJ1dCBtZWRpdW0ga05OIHNjb3JlLgpGb3Iga05OLCB0aGVzZSBwb2ludHMgY2xlYXJseSBoYXZlIGEgaGlnaGVyIGtOTiBkaXN0YW5jZSB0aGFuIHBvaW50cyBpbiB0aGUgY2x1c3RlciBjZW50ZXIuCkhvd2V2ZXIsIHRoZXkgYXJlIG5vdCBmYXIgb2ZmLCByZXN1bHRpbmcgaW4gYSBjb21wYXJhdGl2ZWx5IGxvdyBvdXRsaWVyIHNjb3JlLgpGb3IgTE9GLCB0aGUgbmVpZ2hib3JzIG9mIHRoZXNlIHBvaW50cyBoYXZlIGEgY2xlYXJseSBsb3dlciBkaXN0YW5jZSB0byAqdGhlaXIqIG5laWdoYm9ycywgYmVjYXVzZSB0aGV5IGFyZSBjbG9zZXIgdG8gdGhlIGNlbnRlci4KVGh1cywgdGhlIExPRiBzY29yZSBvZiB0aGUgdHdvIHBvaW50cyB1bmRlciBkaXNjdXNzaW9uIGlzIHJhdGhlciBoaWdoLgoKIyMgYykgU3Vic3BhY2UgT3V0bGllcnMKClRvIGVhc2UgY29tcGFyaXNvbiwgd2UgY3JlYXRlIGEgZ3JpZCB3aXRoIHVuaXZhcmlhdGUgb3V0bGllciBzY29yZXMsIGJpdmFyaWF0ZSBvdXRsaWVyIHNjb3JlcyBhbmQgYml2YXJpYXRlIGRlbnNpdHkuCgpgYGB7ciwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDh9CmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKAogIHBsb3RPdXRsaWVycyhERG91dGxpZXI6OktOTl9TVU0oZGF0YXNldCRlcnVwdGlvbnMsIGsgPSA1KSkgKwogICAgZ2d0aXRsZSgiT3V0bGllcnMgZm9yIFwiZXJ1cHRpb25zXCIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgcGxvdE91dGxpZXJzKEREb3V0bGllcjo6S05OX1NVTShkYXRhc2V0JHdhaXRpbmcsIGsgPSA1KSkgKwogICAgZ2d0aXRsZSgiT3V0bGllcnMgZm9yIFwid2FpdGluZ1wiIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogIHBsb3RPdXRsaWVycyhERG91dGxpZXI6OktOTl9TVU0oZGF0YXNldCwgayA9IDUpKSArCiAgICBnZ3RpdGxlKCJPdXRsaWVycyBmb3IgZnVsbCBzcGFjZSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICBnZ3Bsb3QoZGF0YSA9IGRhdGFzZXQpICsKICAgIGdlb21fYmluMmQobWFwcGluZyA9IGFlcyh4ID0gZXJ1cHRpb25zLCB5ID0gd2FpdGluZyksIGJpbnMgPSAxMCkgKwogICAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsKICAgIGdndGl0bGUoIkRlbnNpdHkiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgbnJvdyA9IDIsIG5jb2wgPSAyLCByZXNwZWN0ID0gVFJVRQopCmBgYAoKVGhlcmUgZGVmaW5pdGVseSBhcmUgcG9pbnRzIHdob3NlIG91dGxpZXIgc2NvcmUgZGlmZmVycyBiZXR3ZWVuIHN1YnNwYWNlcy4KRm9yIGV4YW1wbGUsIHRoZSBwb2ludHMgd2l0aCB0aGUgbG93ZXN0IGFuZCBoaWdoZXN0IHdhaXRpbmcgdGltZXMgaGF2ZSBhIGhpZ2ggb3V0bGllciBzY29yZSByZWdhcmRpbmcgYHdhaXRpbmdgLApidXQgYSBsb3dlciBzY29yZSByZWdhcmRpbmcgYGVydXB0aW9uc2AuCkFsc28sIHRoZXJlIGlzIGEgcG9pbnQgaW4gdGhlIG1pZGRsZSBhdCByb3VnaGx5ICgtMC40LCAtMC4xKSwKd2hpY2ggaXMgcXVpdGUgb3V0bHlpbmcgcmVnYXJkaW5nIGBlcnVwdGlvbnNgIChpdCBpcyB0aGUgb25seSBwb2ludCBpbiBzb21lICJ2ZXJ0aWNhbCBjb3JyaWRvciIgYXJvdW5kIGl0KSwKYnV0IHJhdGhlciBub3JtYWwgcmVnYXJkaW5nIGB3YWl0aW5nYCAoc29tZSBvdGhlciBwb2ludHMgYXJlIGluIHNhbWUgImhvcml6b250YWwgY29ycmlkb3IiIGFyb3VuZCBpdCkuClRoZXJlIGFyZSBhbHNvIG5vbi10cml2aWFsIG91dGxpZXJzLgpGb3IgZXhhbXBsZSwgdGhlIHBvaW50IGF0IHJvdWdobHkgKC0xLCAwKSBpcyBwZXJmZWN0bHkgbm9ybWFsIGluIGVpdGhlciBvbmUtZGltZW5zaW9uYWwgc3BhY2UsCmJ1dCBoYXMgYSBoaWdoIG91dGxpZXIgc2NvcmUgaW4gdGhlIHR3by1kaW1lbnNpb25hbCBzcGFjZS4K